//
//  LcVideoPlayer.m
//  18Birdies
//
//  Created by James Chen on 2019/5/28.
//  Copyright © 2019 SquareValley. All rights reserved.
//

#import "LcVideoPlayer.h"
#import "LcFoundation.h"
#import <AVKit/AVKit.h>
#import "BaseViewController.h"

@interface LcVideoPlayer ()

@property(nonatomic, strong) AVPlayer* avPlayer;
@property(nonatomic, strong) AVPlayerLayer* playerLayer;
@property(nonatomic, strong) id playbackObserver;
@property(nonatomic, assign) BOOL isSeeking;
@property(nonatomic, assign) VideoPlayerState state;
@property(nonatomic, strong) UIActivityIndicatorView* indicatorView;
@end

@implementation LcVideoPlayer

+ (instancetype)videoPLayerWithUrl:(NSURL*)url onScreen:(UIView*)screen {
  LC_CHECK_NOT_NULL(url);
  LcVideoPlayer* player = [[self alloc] init];
  player.url = url;
  player.screen = screen;
  return player;
}

+ (instancetype)videoPLayerWithUrl:(NSURL*)url
                         extension:(NSString*)extension
                          onScreen:(UIView*)screen {
  LC_CHECK_NOT_NULL(url);
  LcVideoPlayer* player = [LcVideoPlayer videoPLayerWithUrl:url onScreen:screen];
  player->_extension = extension;
  return player;
}

+ (instancetype)videoPLayerWithAsset:(AVAsset*)asset onScreen:(UIView*)screen {
  LC_CHECK_NOT_NULL(asset);
  LcVideoPlayer* player = [[self alloc] init];
  player.asset = asset;
  player.screen = screen;
  return player;
}

- (instancetype)init {
  self = [super init];
  self.avPlayer = [[AVPlayer alloc] init];
  self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
  self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
  self.playerLayer.backgroundColor = UIColor.blackColor.CGColor;
  self.indicatorView = [[UIActivityIndicatorView alloc] init];
  self.rate = 1.0;
  [self addObserversForAvPlayer];
  return self;
}

- (void)dealloc {
  LcLog(@"LcVideoPlayer-Dealloc");
}

- (void)setScreen:(UIView*)screen {
  if (screen != nil && self.screen != screen) {
    self.screenChanged = YES;
  }
  [self.playerLayer removeFromSuperlayer];
  [self.indicatorView removeFromSuperview];
  self->_screen = screen;
  if (screen != nil) {
    [self.screen.layer addSublayer:self.playerLayer];
    if (self.screen.subviews.count != 0) {
      self.playerLayer.hidden = YES;
    }
    self.indicatorView.hidden = YES;
    [self.screen addSubview:self.indicatorView];
    [self resizeFrame];
  }
}

- (void)resizeFrame {
  self.playerLayer.frame = self.screen.bounds;
  self.indicatorView.center = CGPointMake(self.screen.width / 2, self.screen.height / 2);
}

- (void)setMuted:(BOOL)muted {
  self->_muted = muted;
  [self.avPlayer setMuted:muted];
}

- (void)setUrl:(NSURL*)url {
  LC_CHECK_NOT_NULL(url);
  self->_url = url;
  self->_asset = nil;
  self->_state = VideoPlayerStateUnknow;
  if ([self currentPlayerItem] != nil) {
    [self removeObserversForPlayerItem];
  }
  NSString* mimeType = @"video/mp4";
  if (self.extension != nil && self.extension.length > 0) {
    mimeType = [NSString stringWithFormat:@"video/%@", self.extension];
  }
  AVPlayerItem* playerItem = [AVPlayerItem
      playerItemWithAsset:[AVURLAsset URLAssetWithURL:self.url
                                              options:@{
                                                @"AVURLAssetOutOfBandMIMETypeKey" : mimeType
                                              }]];
  playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed;
  [self.avPlayer replaceCurrentItemWithPlayerItem:playerItem];
  [self addObserversForPlayerItem];
}

- (void)setAsset:(AVAsset*)asset {
  LC_CHECK_NOT_NULL(asset);
  self->_asset = asset;
  self->_url = nil;
  self->_state = VideoPlayerStateUnknow;
  if ([self currentPlayerItem] != nil) {
    [self removeObserversForPlayerItem];
  }
  AVPlayerItem* playerItem = [AVPlayerItem playerItemWithAsset:asset];
  [self.avPlayer replaceCurrentItemWithPlayerItem:playerItem];
  [self addObserversForPlayerItem];
}

- (void)play {
  if (self.state == VideoPlayerStateFailed) {
    // TODO reload;
    return;
  }
  if (self.playerLayer.hidden) {
    self.playerLayer.hidden = NO;
  }
  if (self.state == VideoPlayerStatePlaying) {
    return;
  }
  [self.avPlayer playImmediatelyAtRate: self.rate];
  self.state = VideoPlayerStatePlaying;
}

- (void)pause {
  if (self.state == VideoPlayerStatePaused) {
    return;
  }
  [self.avPlayer pause];
  self.state = VideoPlayerStatePaused;
}

- (NSArray<NSValue*>*)loadedTimeRanges {
  return self.avPlayer.currentItem.loadedTimeRanges;
}

- (void)setState:(VideoPlayerState)state {
  self->_state = state;
  if (self.stateChangedCallback != nil) {
    self.stateChangedCallback(state);
  }
}

- (void)free {
  self.screen = nil;
  [self removeObserversForPlayerItem];
  [self removeObserversForAvPlayer];
}

- (void)showLoadingIndicator {
  self.indicatorView.hidden = NO;
  [self.indicatorView startAnimating];
}

- (void)hideLoadingIndicator {
  [self.indicatorView stopAnimating];
}

- (AVPlayerItem*)currentPlayerItem {
  return self.avPlayer.currentItem;
}

- (NSString*)videoGravity {
  return self.playerLayer.videoGravity;
}

- (void)setVideoGravity:(NSString *)videoGravity {
  self.playerLayer.videoGravity = videoGravity;
}

- (double)totalSeconds {
  CMTime duration = self.currentPlayerItem.duration;
  double totalSeconds = CMTimeGetSeconds(duration);
  if (isnan(totalSeconds)) {
    return 0l;
  }
  return totalSeconds;
}

- (double)currentSeconds {
  CMTime currentTime = self.currentPlayerItem.currentTime;
  double currentSeconds = CMTimeGetSeconds(currentTime);
  double totalSeconds = self.totalSeconds;
  if (currentSeconds < 0) {
    currentSeconds = 0;
  } else if (currentSeconds > totalSeconds) {
    currentSeconds = totalSeconds;
  }
  return currentSeconds;
}

- (void)setCurrentSeconds:(double)currentSeconds {
  LC_CHECK_TRUE(currentSeconds >= 0);
  LcWeakSelf;
  self.isSeeking = YES;
  [self.avPlayer seekToTime:CMTimeMakeWithSeconds(currentSeconds, 1000)
            toleranceBefore:kCMTimeZero
             toleranceAfter:kCMTimeZero
          completionHandler:^(BOOL finished) {
            LcStrongSelf;
            self.isSeeking = NO;
          }];
}

- (void)addObserversForAvPlayer {
  [self.avPlayer addObserver:self forKeyPath:@"timeControlStatus" options:0 context:nil];
  LcWeakSelf;
  CMTime interval = CMTimeMakeWithSeconds(0.5, NSEC_PER_SEC);
  self.playbackObserver =
      [self.avPlayer addPeriodicTimeObserverForInterval:interval
                                                  queue:dispatch_get_main_queue()
                                             usingBlock:^(CMTime time) {
                                               LcStrongSelf;
                                               [self avPlayerTimeChangesCallBack];
                                             }];
}

- (void)removeObserversForAvPlayer {
  [self.avPlayer removeObserver:self forKeyPath:@"timeControlStatus"];
  [self.avPlayer removeTimeObserver:self.playbackObserver];
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)addObserversForPlayerItem {
  self.replayedTimes = 0;
  [self.currentPlayerItem addObserver:self
                           forKeyPath:@"status"
                              options:NSKeyValueObservingOptionNew
                              context:nil];
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(videoDidFinishPlaying:)
                                               name:AVPlayerItemDidPlayToEndTimeNotification
                                             object:self.currentPlayerItem];
}

- (void)removeObserversForPlayerItem {
  [self.currentPlayerItem removeObserver:self forKeyPath:@"status"];
  [[NSNotificationCenter defaultCenter] removeObserver:self
                                                  name:AVPlayerItemDidPlayToEndTimeNotification
                                                object:self.currentPlayerItem];
}

- (void)observeValueForKeyPath:(NSString*)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey, id>*)change
                       context:(void*)context {
  if ([keyPath isEqualToString:@"status"]) {
    AVPlayerItem* playerItem = self.currentPlayerItem;
    if (playerItem.status == AVPlayerItemStatusReadyToPlay) {
      self.state = VideoPlayerStateReady;
      if (self.playWhenReady) {
        [self play];
      }
    } else if (playerItem.status == AVPlayerItemStatusFailed) {
      self.state = VideoPlayerStateFailed;
#ifdef DEBUG
      [LcToast showToastWithMessage:[NSString stringWithFormat:@"%@", playerItem.error.description]
                       durationType:TOAST_DURATION_LONG];
#endif
    }
  } else if ([keyPath isEqualToString:@"timeControlStatus"]) {
    if (self.avPlayer.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
      [self showLoadingIndicator];
    } else {
      [self hideLoadingIndicator];
    }
    if (self.timeControlStatusChangedCallback != nil) {
      self.timeControlStatusChangedCallback(self.avPlayer.timeControlStatus);
    }
  }
}

- (void)avPlayerTimeChangesCallBack {
  if (self.isSeeking) {
    return;
  }
  if (self.totalSeconds == 0) {
    return;
  }
  if (self.timeChangedCallback != nil) {
    self.timeChangedCallback(self.currentSeconds);
  }
}

- (void)videoDidFinishPlaying:(NSNotification*)notification {
  self.state = VideoPlayerStateFinished;
  if (self.replayWhenFinish) {
    self.currentSeconds = 0;
    self.replayedTimes += 1;
  }
}

@end
